import asyncio            # 导入异步IO库，用于支持异步编程
import websockets         # 导入WebSocket库，提供WebSocket服务器功能
import socket             # 导入socket库，用于获取本地IP地址
import json               # 导入JSON库，用于解析消息
import time               # 导入时间库，用于时间戳
from aiohttp import web   # 导入Web服务器

# 设备管理字典（设备信息）
connected_devices = {}

# 任务管理字典（任务信息）
task_data = {}

def get_timestamp():
    """返回格式化的当前时间"""
    return time.strftime("%H:%M:%S", time.localtime())

def format_device_id(device_id):
    """格式化设备ID，只显示前6位和后4位"""
    if len(device_id) > 10:
        return f"{device_id[:6]}...{device_id[-4:]}"
    return device_id

async def echo(websocket):
    try:
        # 处理每个客户端连接的协程函数
        # websocket参数是一个与客户端的WebSocket连接对象
        async for message in websocket:  # 异步迭代接收客户端消息

            # 尝试解析JSON消息并根据path字段处理
            try:
                data = json.loads(message)       # 解析JSON消息
                
                # 检查是否有设备ID
                if 'device_id' in data:
                    device_id = data['device_id']              # 获取设备ID（设备唯一标识符）
                    short_id = format_device_id(device_id)      #格式化设备ID
                    
                    # 检查消息路径是否是AS的设备信息
                    if data.get('path') == 'device_info':

                        # 获取设备信息
                        result = data.get('result', {})                # 获取结果
                        device_name = result.get('device_name', '')    # 设备名称
                        app_is_run = result.get('app_is_run', False)   # 应用是否运行
                        app_id = result.get('app_id', '')              # 应用ID（小程序名称）
                        
                        # 第三阶段：检查是否有新消息数据(app_data)
                        if 'app_data' in result:
                            app_data = result['app_data']               # 获取应用的消息数据
                 
                            # 打印app_data信息
                            data_str = ", ".join([f"{k}: {v}" for k, v in app_data.items()])
                            print(f"[{get_timestamp()}] 📱 设备消息 | {device_name} ({short_id}) | 应用: {app_id} | 数据: {data_str}")

                            
                            #消息类型分类
                            if 'Task_message' in app_data:
                                print("这是任务信息")

                                #获取任务ID
                                task_id = app_data['Task_id']

                                #将任务信息添加到任务管理字典中
                                task_data[task_id] = app_data

                                print(task_data)

                            else:
                                print("这是其他信息")
               
                        
                        # 第一阶段：新设备连接服务器
                        if device_id not in connected_devices:  # 如果设备ID不在连接设备列表中

                            # 将设备信息添加到连接设备列表中
                            connected_devices[device_id] = {
                                'device_id': device_id,              # 设备ID（设备唯一标识符）
                                'device_name': device_name,          # 设备名称
                                'app_is_run': app_is_run,            # 应用是否运行
                                'app_id': app_id,                    # 应用ID（小程序名称）
                                'last_seen': time.time()             # 最后看到时间
                            }
                            print(f"[{get_timestamp()}] ✅ 设备连接 | {device_name} ({short_id})")

                            # 打印连接后的设备管理字典
                            print("连接后的设备管理字典：",connected_devices)
                        
                        # 第二阶段：更新设备状态（启动程序）
                        else:
                            connected_devices[device_id]['app_is_run'] = app_is_run  # 更新应用是否运行
                            connected_devices[device_id]['app_id'] = app_id          # 更新应用ID（小程序名称）
                            connected_devices[device_id]['last_seen'] = time.time()  # 更新最后看到时间
                            
                            # 如果应用刚启动
                            if app_is_run and not connected_devices[device_id].get('was_running', False):
                                print(f"[{get_timestamp()}] 🚀 应用启动 | {device_name} ({short_id}) | 应用: {app_id}")

                                connected_devices[device_id]['was_running'] = True  # 更新应用是否运行

                                # 打印更新后的设备管理字典
                                print("更新后的设备管理字典：",connected_devices)
                            
                        # 第四阶段：设备离线处理
                        if not app_is_run and device_id in connected_devices and connected_devices[device_id].get('was_running', False):
                            print(f"[{get_timestamp()}] 🔌 应用关闭 | {device_name} ({short_id})")
                            # 从设备管理中移除
                            del connected_devices[device_id]

                            # 打印移除后的设备管理字典
                            print("移除后的设备管理字典：",connected_devices)
                        
                        # 回复确认消息
                        response = {
                            "msg": "success",               # 消息类型
                            "path": "device_info_ack",      # 消息路径
                            "device_id": device_id          # 设备ID（设备唯一标识符）
                        }
                        await websocket.send(json.dumps(response))  # 异步发送回复给客户端

            except json.JSONDecodeError:
                # 不是JSON格式，按原样回复
                await websocket.send(f"服务器回复: {message}")  # 异步发送回复给客户端

    except websockets.exceptions.ConnectionClosedError:
        # 处理连接关闭
        print(f"[{get_timestamp()}] 🔴 连接断开 | 客户端意外断开连接")
    except Exception as e:
        # 处理其他异常
        print(f"[{get_timestamp()}] ⚠️ 错误 | {type(e).__name__}: {str(e)}")
    finally:
        # 清理代码：可以在这里检查和清理失去连接的设备
        # 例如，遍历connected_devices找出对应的设备并移除

        del connected_devices[device_id]  # 移除设备管理字典中的设备
        print("移除后的设备管理字典：",connected_devices)

        pass

# API端点处理函数
async def get_devices(request):
    """获取所有在线设备的信息"""
    # 只返回app_is_run为True的设备
    online_devices = {k: v for k, v in connected_devices.items() if v.get('app_is_run', False)}
    
    return web.json_response({
        "status": "success",          # 状态（成功）
        "count": len(online_devices),  # 在线设备数量
        "devices": online_devices    # 在线设备列表（设备信息）
    })

def get_local_ip():
    # 获取本机在局域网中的IP地址
    try:
        # 创建UDP套接字（不实际建立连接）
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        # 尝试连接到谷歌DNS服务器，这会使系统选择合适的网卡和IP
        s.connect(("8.8.8.8", 80))
        # 获取选择的IP地址
        ip = s.getsockname()[0]
        # 关闭套接字
        s.close()
        return ip
    except Exception:
        # 如果获取失败（例如没有网络连接），则返回本地回环地址
        return "127.0.0.1"

async def main():
    # 主协程函数，启动WebSocket服务器和API服务器
    host = "0.0.0.0"      # 监听所有网络接口，允许外部设备连接
    ws_port = 8766        # WebSocket服务器端口号
    api_port = 8767       # API服务器端口号
    
    local_ip = get_local_ip()  # 获取本机局域网IP地址
    
    # 创建API应用
    app = web.Application()                          # 创建应用
    app.router.add_get('/api/devices', get_devices)  # 添加API路由(api接口)
    
    # 启动API服务器
    runner = web.AppRunner(app)                     # 创建应用运行器
    await runner.setup()                            # 设置运行器
    api_site = web.TCPSite(runner, host, api_port)  # 创建TCP站点
    await api_site.start()                          # 启动站点
    
    # 创建并启动WebSocket服务器，echo函数为连接处理器
    async with websockets.serve(echo, host, ws_port):
        # 构造WebSocket连接URL
        ws_url = f"ws://{local_ip}:{ws_port}"        # 外部设备可用的WebSocket地址
        local_ws_url = f"ws://localhost:{ws_port}"   # 本机可用的WebSocket地址
        
        # 构造API连接URL
        api_url = f"http://{local_ip}:{api_port}/api/devices"      # 外部设备可用的API地址
        local_api_url = f"http://localhost:{api_port}/api/devices" # 本机可用的API地址
        
        # 打印服务器启动信息
        print(f"\n{'='*70}")
        print(f"{'📡 WebSocket & API 服务器启动成功 ':^70}")
        print(f"{'='*70}")
        print(f"📅 启动时间: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())}")
        print(f"{'- '*35}")
        print(f"⚡ WebSocket服务器")
        print(f"   • 本地访问: {local_ws_url}")
        print(f"   • 网络访问: {ws_url}")
        print(f"{'- '*35}")
        print(f"🌐 API服务器")
        print(f"   • 本地访问: {local_api_url}")
        print(f"   • 网络访问: {api_url}")
        print(f"{'- '*35}")
        print(f"💡 提示: 扫描以上地址二维码可快速连接")
        print(f"{'='*70}\n")
        
        await asyncio.Future()  # 永久运行服务器，直到程序被强制终止

if __name__ == "__main__":
    # 当直接运行此脚本时执行以下代码
    asyncio.run(main())  # 启动异步事件循环，运行main协程 